refactor(i18n)!: consume the shared runtime from @stream-io/i18n - #3284
Conversation
BREAKING CHANGE: requires `@stream-io/i18n` and `stream-chat@^10.0.0-rc.10`. Retargets every `stream-chat/i18n` import to `@stream-io/i18n`, which is where the shared runtime now lives. The API is unchanged; only the specifier moves. `languageNameDefaults` / `LanguageNameCatalog` come from `stream-chat`'s root barrel instead — they enumerate the languages the Chat API can auto-translate a message into, so they stayed with Chat rather than moving to the generic package. `TranslationContext` and `useStreami18n` are now built on the shared bindings in `@stream-io/i18n/react`, so this SDK and the React Native one no longer carry two copies of the same context plumbing and store subscription. What stays here is what is genuinely this SDK's: the catalog-typed `t`, the notification translation topic, and supplying a context *default* rather than throwing, which is what lets a primitive render outside `<Chat>`. `@stream-io/i18n` is a regular dependency, not a peer: an integrator never imports it — they import the `Streami18n` this package subclasses and re-exports. Also fixes `yarn types`, which silently checked nothing. It ran `tsc` with no `--project`, picking up the root solution file (`"files": []`), and exited 0 even with a deliberate error in `src/`. It is now `tsc -p tsconfig.lib.json --noEmit` and is wired into CI, which previously typechecked only the build scripts.
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Advanced Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
BREAKING CHANGE: adds `@stream-io/state-store` as a dependency. `stream-chat` no longer re-exports `StateStore`, so the 22 modules that imported it from there now take it from the package that defines it. No behaviour change — the store instances are the same objects, only the import specifier moves. A single shared declaration matters here: TypeScript compares classes with `protected` members nominally, so two copies would not be assignable to one another and `useStateStore` would stop typechecking against stores handed over by `stream-chat` or `@stream-io/i18n`.
The previous commit stopped `stream-chat` from exporting `StateStore`, and updated every call site in `src/` -- but not the Vite example, which is the reference integration and is built and deployed by CI on every PR. `examples/vite/src/AppSettings/state.ts` constructs one at module scope, so this was a runtime break, not just a type error: `StateStore` resolves to `undefined` and the app throws `TypeError: StateStore is not a constructor` on boot. Rollup catches it first -- `[MISSING_EXPORT] "StateStore" is not exported by stream-chat` -- so `yarn build` in the example fails outright. Two related fixes in the example manifests: - `@stream-io/state-store` is now declared, rather than relied on by hoisting. - `stream-chat` was pinned exactly at `10.0.0-rc.9` in both examples, which no longer satisfies this package's `^10.0.0-rc.10` peer range. Loosened to a caret. Also aligns `dayjs` at `^1.11.23` to match `@stream-io/i18n`. The ranges had drifted apart (`^1.11.13` here, `^1.11.20` in the example, `^1.11.23` in the package), and dayjs locale registration is global: a second resolved copy means an integrator's `import 'dayjs/locale/de'` extends an instance the SDK never formats with, and dates stay English with nothing thrown. CLAUDE.md asserted the old range, so it is corrected too.
The package released as 1.0.1, not 1.0.0 -- the version PR consumed 1.0.0 while publishing was still broken, so it never reached npm. `^1.0.0` resolves to 1.0.1 today, so this is accuracy rather than a fix: it stops the manifest declaring a floor that does not exist on the registry.
First lockfile that resolves both packages from the registry rather than from
a local `file:` tarball: `@stream-io/i18n@1.0.1` and `stream-chat@10.0.0-rc.10`.
No `.local-links` entries remain.
`scripts/generate-i18n-keys.mts` switches to `import * as ts from 'typescript'`.
The default import stopped satisfying `TypeScriptModule` once the generator
moved into `@stream-io/i18n`, because marking `dist/codegen` as `type: module`
(so the ESM-only entry point loads on any Node) also makes TypeScript read its
`.d.ts` files as ESM. Under `nodenext`, a namespace import of a CJS `export =`
module is then modelled with a synthetic `default`, so the exported
`typeof ts` requires a `default` property that the default import does not have:
error TS2741: Property 'default' is missing in type
'typeof import(".../typescript")' but required in type 'typeof ts'
Verified this is specifically the nested manifest: with it removed from
node_modules the default import type-checks, with it present it does not. The
same probe against stream-chat rc.9's copy of the identical declaration passes,
so it is the packaging change rather than the type text.
A namespace import is the correct shape here anyway -- it is what the package's
own declaration uses -- and `yarn build-translations` regenerates `keys.ts`
byte-identically under it. The package-side fix, so the default import keeps
working for every consumer, is filed separately.
|
Size Change: -2.96 kB (-0.36%) Total Size: 820 kB 📦 View Changed
ℹ️ View Unchanged
|
… workaround 1.0.2 fixes `TypeScriptModule`, which 1.0.1 had declared as `typeof ts` over a namespace import. Because `./codegen` ships a nested manifest marking the directory ESM, TypeScript read its `.d.ts` as ESM, where a namespace import of a CJS `export =` module carries a synthetic `default` -- so the exported type required a property the plain default import does not have. It is now an explicit `Pick` of the members the generator actually uses, which reads the same in every module mode. So `scripts/generate-i18n-keys.mts` goes back to `import ts from 'typescript'`, matching the style it had before the move and the one the React Native SDK uses. `yarn types:scripts` passes with it, and `build-translations` regenerates `keys.ts` byte-identically.
Retargets every
stream-chat/i18nimport to@stream-io/i18n, where the shared runtime now lives. The API is unchanged — only the specifier moves.@stream-io/i18n@1.0.0andstream-chat@10.0.0-rc.10are not on npm yet, soyarn.lockcannot be regenerated and is deliberately left untouched in this PR. Everything else is reviewable now. Locally this was verified against packed tarballs of both.What changed
stream-chat/i18nimport →@stream-io/i18n(5 files plus the codegen script).languageNameDefaults/LanguageNameCatalognow come fromstream-chat's root barrel — they enumerate the languages the Chat API can auto-translate a message into, so they stayed with Chat.TranslationContextanduseStreami18nare built on the shared bindings in@stream-io/i18n/react, so this SDK and React Native stop carrying two copies of the same context plumbing and store subscription.What stays this SDK's own: the catalog-typed
t, the notification translation topic, and supplying a context default rather than throwing — which is what lets a primitive render outside<Chat>(React Native throws instead, which is why the shared factory takes that as an option).@stream-io/i18nis a regular dependency, not a peer: an integrator never imports it — they import theStreami18nthis package subclasses and re-exports.Also fixes:
yarn typeschecked nothingIt ran
tscwith no--project, so it picked up the root solution file ("files": []) and exited 0 even with a deliberate type error insrc/. It is nowtsc -p tsconfig.lib.json --noEmit, verified to fail on a planted error, and added to CI — which previously typechecked only the build scripts, so a library type error could reachmaster.CLAUDE.md/AGENTS.mdupdated accordingly (they documented the trap rather than fixing it).Verification
yarn types(the real one),yarn lint, and 2,769 tests all pass against locally-linked builds of@stream-io/i18nandstream-chat. Also verified end-to-end in the Vite example: switching language at runtime updates copy, plurals, placeholders, aria-labels and dates —Tue, 2 Aug→Di., 2. Aug.,08/02/2022→02.08.2022— with no reload.Merge order
After js-toolkit#51 and stream-chat-js#1861, then rerun
yarn installto commit a real lockfile.